﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using Fasterflect;
using LL.Common;
using LL.Data;
using LL.Data.FluentData;
using LL.Data.FluentData.Mapping;
using FluentData;

namespace LL.Data.FluentData
{
    /// <summary>
    /// 工作单元
    /// </summary>
    public class DbSession : DbSessionBase, IDbSession
    {
        #region 构造

        public DbSession(IDbContext dbContext)
        {
            this.DbContext = dbContext;
        }

        #endregion

        #region 私有方法

        private static string GetKeyEqualStr(string[] keyNames)
        {
            var str = new StringBuilder();
            for (int i = 0; i < keyNames.Length; i++)
            {
                var keyName = keyNames[i];
                str.AppendFormat("{0}=@{1} {2}", keyName, i,
                    i.Equals(keyNames.Length - 1) ? "" : " AND ");
            }
            return str.ToString();
        }

        private object[] GetKeyEqualVals(string[] keyNames, object entity)
        {
            return (from d in keyNames select entity.GetPropertyValue(d)).ToArray();
        }

        /// <summary>
        /// 计算分页后总页数
        /// </summary>
        protected virtual int GetTotalPagesCount(int pageSize, string sql, params object[] parameters)
        {
            var sqlStr = string.Format("SELECT CEILING(COUNT(1)/CAST({0} AS FLOAT)) FROM ({1}) _T", pageSize, sql);
            return this.DbContext
                    .Sql(sqlStr, parameters)
                    .QuerySingle<int>();
        }

        /// <summary>
        /// 
        /// </summary>
        protected virtual int GetTotalRowsCount(string sql, params object[] parameters)
        {
            var sqlStr = string.Format("SELECT COUNT(1) FROM ({0}) _T", sql);
            return this.DbContext
                    .Sql(sqlStr, parameters)
                    .QuerySingle<int>();
        }

        #endregion

        #region IRepository

        public TEntity Get<TEntity>(params object[] key)
        {
            var mapper = MapperFactory.GetMapper<TEntity>();
            string sql = string.Format("SELECT TOP(1) {0} FROM {1} WHERE {2}",
                mapper.GetColumnsNames().Combine(','),
                mapper.GetTableName(),
                GetKeyEqualStr(mapper.GetPrimaryKeyNames()));
            return this.DbContext
                 .Sql(sql, key)
                 .QuerySingle<TEntity>();
        }

        public int Delete<TEntity>(params object[] key)
        {
            var mapper = MapperFactory.GetMapper<TEntity>();
            string sql = string.Format("DELETE FROM {0} WHERE {1}",
                mapper.GetTableName(),
                GetKeyEqualStr(mapper.GetPrimaryKeyNames()));
            return this.DbContext
                 .Sql(sql, key)
                 .Execute();
        }

        public int Delete(object entity)
        {
            var mapper = MapperFactory.GetMapper(entity);
            var keyNames = mapper.GetPrimaryKeyNames();
            var query = this.DbContext
                .Delete(mapper.GetTableName());
            foreach (var item in keyNames)
            {
                query.Where(item, entity.GetPropertyValue(item));
            }
            return query.Execute();
        }

        public int Delete<TEntity>(string where, params object[] parameters)
        {
            if (where.IsNullOrEmpty())
                throw new ArgumentException("where statement can not be null or empty");
            var mapper = MapperFactory.GetMapper<TEntity>();
            return ExcuteNonQuery(
                string.Format("DELETE FROM {0} WHERE {1}",
                mapper.GetTableName(), where), parameters);
        }

        public int Insert(object entity)
        {
            var mapper = MapperFactory.GetMapper(entity);
            return this.DbContext
                 .Insert(mapper.GetTableName(), entity)
                 .AutoMap(mapper.GetIgnoreColumnNames().Union(
                        mapper.GetAutoIncrementColumnNames()).ToArray())
                 .Execute();
        }

        public int Update(object entity)
        {
            var mapper = MapperFactory.GetMapper(entity);
            var keyNames = mapper.GetPrimaryKeyNames();
            var query = this.DbContext
                .Update(mapper.GetTableName(), entity)
                //↓Update时需要忽略主键和实体中标注Ignore的属性列
                .AutoMap(mapper.GetPrimaryKeyNames().Union(mapper.GetIgnoreColumnNames()).ToArray());
            foreach (var item in keyNames)
            {
                query.Where(item, entity.GetPropertyValue(item));
            }
            return query.Execute();
        }

        public bool Exist<TEntity>(params object[] key)
        {
            var mapper = MapperFactory.GetMapper<TEntity>();

            string sql = string.Format("SELECT COUNT(1) FROM {0} WHERE {1}",
                mapper.GetTableName(),
                GetKeyEqualStr(mapper.GetPrimaryKeyNames()));
            return this.DbContext
                 .Sql(sql, key)
                 .QuerySingle<int>() > 0;
        }

        public bool Exist(object entity)
        {
            var mapper = MapperFactory.GetMapper(entity);

            var keys = mapper.GetPrimaryKeyNames();
            string sql = string.Format("SELECT COUNT(1) FROM {0} WHERE {1}",
                mapper.GetTableName(),
                GetKeyEqualStr(keys));
            return this.DbContext
                 .Sql(sql, GetKeyEqualVals(keys, entity))
                 .QuerySingle<int>() > 0;
        }

        public IList<TEntity> GetAll<TEntity>()
        {
            return this.Query<TEntity>("");
        }

        public IList<TEntity> Query<TEntity>(string where,params object[] parameters)
        {
            var mapper = MapperFactory.GetMapper<TEntity>();
            string sql = string.Format("SELECT {0} FROM {1} WHERE {2}",
                mapper.GetColumnsNames().Combine(','),
                mapper.GetTableName(),
                where.Default("1=1"));

            return this.DbContext
                       .Sql(sql, parameters)
                       .QueryMany<TEntity>();
        }

        public IList<TEntity> QueryWithPaging<TEntity>(int pageIndex, int pageSize, out int totalRows, string orderBy = null, string where = null,
                                              params object[] parameters)
        {
            var mapper = MapperFactory.GetMapper<TEntity>();
            string sql = string.Format("SELECT {0} FROM {1} WHERE {2}",
                mapper.GetColumnsNames().Combine(','),
                mapper.GetTableName(),
                where.Default("1=1"));

            totalRows = GetTotalRowsCount(sql, parameters);
            return totalRows == 0 ? new List<TEntity>(1) : DbContext
                .Select<TEntity>(mapper.GetColumnsNames().Combine(','))
                .From(mapper.GetTableName())
                .Where(where)
                .Parameters(parameters)
                .OrderBy(orderBy.Default(mapper.GetPrimaryKeyNames()[0]))
                .Paging(pageIndex + 1, pageSize)
                .QueryMany();
        }

        #endregion

        #region ISqlHelper

        public int ExcuteNonQuery(string sql, params object[] parameters)
        {
            return DbContext.Sql(sql, parameters).Execute();
        }

        public DataTable ExcuteQuery(string sql, params object[] parameters)
        {
            return DbContext.Sql(sql, parameters).QuerySingle<DataTable>();
        }

        public T ExcuteScalar<T>(string sql, params object[] parameters)
        {
            return DbContext.Sql(sql, parameters).QuerySingle<T>();
        }

        public DataTable StoredProcdure(string procdureName, params object[] parameters)
        {
            var str = new StringBuilder();
            str.AppendFormat("Exec {0}", procdureName);
            if (parameters == null)
                return this.DbContext.Sql(str.ToString()).QuerySingle<DataTable>();
            for (int i = 0; i < parameters.Length; i++)
            {
                str.AppendFormat(" @{0}{1}", i, i == (parameters.Length - 1) ? "" : ",");
            }
            return this.DbContext.Sql(str.ToString(), parameters).QuerySingle<DataTable>();
        }

        /// <summary>
        /// 查询
        /// </summary>
        public IList<T> SqlQuery<T>(string sql, params object[] parameters)
        {
            return DbContext
                .Sql(sql)
                .Parameters(parameters)
                .QueryMany<T>();
        }

        #endregion

        /// <summary>
        /// 标准查询
        /// </summary>
        /// <typeparam name="T">查询的数据类型</typeparam>
        /// <param name="request">查询请求</param>
        /// <returns>查询结果</returns>
        public LoadDataResponse<T> Query<T>(LoadDataRequest request)
        {
            //构建返回数据
            var returnData = new LoadDataResponse<T> { Request = request };
            //翻译Sql语句
            var transResult = SqlTranslator.Instance.Translate(request.QueryCondition);
            //翻译排序
            var order = SqlTranslator.TranslateOrder(request.Order);
            //分页查询
            if (request.PaggingInfo != null)
            {
                //分页数据
                var page = request.PaggingInfo;
                int totalRows;
                //查询
                returnData.Data = QueryWithPaging<T>(page.PageIndex, page.PageSize, out totalRows,
                    order, transResult.WhereStatement, transResult.Parameters.ToArray());
                //总数据量
                returnData.TotalRows = totalRows;
                returnData.IsPaged = true;
                return returnData;
            }
            //不分页查询
            returnData.Data = Query<T>(transResult.WhereStatement, order, transResult.Parameters);
            return returnData;
        }

        public int Insert(object entity, params string[] ignoreProperties)
        {
            var mapper = MapperFactory.GetMapper(entity);
            return this.DbContext
                 .Insert(mapper.GetTableName(), entity)
                 .AutoMap(ignoreProperties)
                 .Execute();
        }
    }
}
